package top.superflow.flow.config.dependency

import org.apache.commons.lang3.builder.ToStringBuilder
import top.superflow.exception.RequestUnsupportedException
import top.superflow.flow.config.Identifiable
import top.superflow.flow.runtime.cache.FlowRunTimeCache
import top.superflow.flow.runtime.processprovider.RuntimeProcessProvider
import top.superflow.flow.runtime.resource.RuntimeResource
import top.superflow.lang.{CollectionUtils, StringUtils}

import scala.collection.mutable

class DependencyManager private(){

  private final val EXPECTED_DEPENDENCY_CATEGORY_RESOURCE = "resource"

  private final val EXPECTED_DEPENDENCY_CATEGORY_PROCESS_PROVIDER = "provider"

  def getDependency[T <: Identifiable](dependencyConfigs : List[DependencyConfig]) : Map[String, T] = {
    if(CollectionUtils.isEmpty(dependencyConfigs)){
      return Map[String, T]()
    }

    val dependencies = mutable.Map[String, T]()

    for(dependencyConfig <- dependencyConfigs){
      val dependency = FlowRunTimeCache.getInstance.get(dependencyConfig.getGroup, dependencyConfig.getKey, getType(dependencyConfig.getCategory))
      if(dependency == null){
        throw new RequestUnsupportedException("There is corresponding dependeny created for [" + ToStringBuilder.reflectionToString(dependencyConfig) + "]")
      }
      collectionDependencyProperly(dependencies, dependencyConfig, dependency)
    }

    dependencies.toMap
  }


  private def collectionDependencyProperly[T <: Identifiable](dependencies: mutable.Map[String, T], dependencyConfig: DependencyConfig, dependency: Identifiable) :Unit = {
    val targetDependency = dependency.asInstanceOf[T]
    if (CollectionUtils.isEmpty(dependencyConfig.getMapping)) {
      dependencies.put(dependency.getKey, targetDependency)
      return;
    }

    for(mapping <- dependencyConfig.getMapping){
      if(StringUtils.isNotBlank(mapping)){
        dependencies.put(mapping, targetDependency)
      }
    }
  }

  def getDependedProvider(dependencyConfigs : List[DependencyConfig]) : RuntimeProcessProvider = {
    if(CollectionUtils.isEmpty(dependencyConfigs)){
      return null
    }

    val dependencyConfig = dependencyConfigs.apply(0)
    val dependency = FlowRunTimeCache.getInstance.get(dependencyConfig.getGroup, dependencyConfig.getKey, getType(dependencyConfig.getCategory))
    if(dependency == null){
      throw new RequestUnsupportedException("There is corresponding dependeny created for [" + ToStringBuilder.reflectionToString(dependencyConfig) + "]")
    }
    return dependency.asInstanceOf[RuntimeProcessProvider]
  }

  private def getType(typeDesc : String): Class[_ <: Identifiable] = {
    if(StringUtils.equalsIgnoreCase(EXPECTED_DEPENDENCY_CATEGORY_RESOURCE, typeDesc)){
      return classOf[RuntimeResource]
    }

    if(StringUtils.equalsIgnoreCase(EXPECTED_DEPENDENCY_CATEGORY_PROCESS_PROVIDER, typeDesc)){
      return classOf[RuntimeProcessProvider]
    }

    throw new RequestUnsupportedException("[" + typeDesc + "] is not a recognized category for the dependency.")
  }

  def getDependency[T](expectedKey : String, expectedType : Class[T], dependencies : Map[String, Any]) : Any = {
    if(StringUtils.isBlank(expectedKey) || expectedType == null){
      throw new RequestUnsupportedException("The expected key or the expected type should not be null")
    }

    validateManifest(expectedKey, dependencies)

    val expectedDependencyOpt = dependencies.get(expectedKey)


    if(expectedDependencyOpt.isEmpty || ! expectedType.isAssignableFrom(expectedDependencyOpt.get.getClass)) {
      throw new RequestUnsupportedException("The type is not expected as or sub of [" + expectedType.getName + "]")
    }

    expectedDependencyOpt.get.asInstanceOf[T]
  }

  def validateManifest(expectedKey : String, dependencies : Map[String, _]): Unit ={
    if(CollectionUtils.isEmpty(dependencies) || !dependencies.contains(expectedKey)){
      throw new RequestUnsupportedException("There is no expected dependency which is named as [" + expectedKey + "]")
    }
  }

//  def getDependedResource()

}

object DependencyManager{
  private final val instance = new DependencyManager

  def getInstance = instance
}
